package com.kingmed.kmss.bpmn.flow.service.msg;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.kingmed.kmss.bpmn.config.CrmCpWxConfig;
import com.kingmed.kmss.bpmn.modules.backup.model.BizApprovalStatus;
import com.kingmed.kmss.bpmn.modules.backup.service.BizApprovalStatusService;
import com.kingmed.kmss.common.constant.bpmn.ProcessConstant;
import com.kingmed.kmss.common.constant.common.CommonConstants;
import com.kingmed.kmss.common.constant.marketing.MktConstants;
import com.kingmed.kmss.common.dto.admin.sys.SysMsgCreateDTO;
import com.kingmed.kmss.common.dto.admin.sys.SysMsgDTO;
import com.kingmed.kmss.common.dto.admin.ums.AdminDTO;
import com.kingmed.kmss.common.dto.auth.CustomMessageReq;
import com.kingmed.kmss.common.enums.admin.MsgBizSourceEnum;
import com.kingmed.kmss.common.enums.admin.MsgTypeEnum;
import com.kingmed.kmss.common.utils.AppContextUtil;
import com.kingmed.kmss.framework.cpwx.config.WxCpProperties;
import com.kingmed.kmss.framework.cpwx.service.CpWxAgentMessageService;
import com.kingmed.kmss.framework.feign.client.admin.CommonAdminFeignServiceFacade;
import com.kingmed.kmss.framework.feign.client.auth.CommonAuthFeignFacade;
import com.kingmed.kmss.framework.feign.client.dict.DictFeignServiceFacade;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.bean.message.WxCpMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class BpmnMsgServiceImpl {
	public static final String OA_MSG_STATE_INIT = "0";
	@Autowired
	DictFeignServiceFacade dictFeignSvc;
	@Autowired
    BizApprovalStatusService bizStatusSvc;
	@Autowired
	CommonAdminFeignServiceFacade adminFeignSvc;
	@Autowired
	CommonAuthFeignFacade authFeignSvc;
	@Autowired
	CpWxAgentMessageService cpWxAgentMessageService;
	@Autowired
	private CrmCpWxConfig crmCpWxConfig;
	@Autowired
	private WxCpProperties wxCpProperties;
	// 企微url回调路径
	private static String CRM_CPWX_CALLBACK_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=crm&agentid=%s#wechat_redirect";
	private static String CRM_CALLBACK_URL= "%s%s?bizUrl=%s&agentId=%s";
	private static String BPMB_URL = "%s/#/approve?bizType=%s&isapp=true";
	// 消息通知格式
	private static String TITLE_FORMAT = "【%s】";
	private static String CONTENT_FORMAT_SUBMITTED = "【%s】 待您处理，请及时查看。";
	private static String CONTENT_FORMAT_REJECTED = "【%s】 被驳回，处理人是【%s】  【%s】，请及时查看。";
	private static String CONTENT_FORMAT_APPROVED = "【%s】 完成最终审批，请及时查看。";
	
	public void send(String businessType, String businessId, BizApprovalStatus bizStatus, List<String> groupUsers, String procInstId) {
		// 如果 bizStatus 为空，要补充
		if (bizStatus == null) {
			bizStatus = bizStatusSvc.getApprovalStatusByTypeAndId(businessType, businessId, procInstId);
			if (bizStatus == null) {
				log.warn("流程状态为空");
				return;
			}
		}
		// 如果groupUsers为空,要查询一次补充
		if (CollUtil.isEmpty(groupUsers)) {
			if (ProcessConstant.Status.REJECTED.equals(bizStatus.getCurrStatus())
					|| ProcessConstant.Status.APPROVED.equals(bizStatus.getCurrStatus())) {
				groupUsers = bizStatus.getRequestBy() != null? Arrays.asList(bizStatus.getRequestBy().split(MktConstants.LIST_STRING_SEPERATOR)):null;
			} else {
				groupUsers = bizStatus.getNextApprover() != null? Arrays.asList(bizStatus.getNextApprover().split(MktConstants.LIST_STRING_SEPERATOR)):null;
			}
		}
		sendCrmSysMsg(businessType, businessId, bizStatus, groupUsers, procInstId);

		// 避免因oa消息发送异常导致主流程失败
		try {
			sendOaMsg(businessType, businessId, bizStatus, groupUsers, procInstId);
		} catch (Exception e) {
			log.error("发送oa消息异常：" + e.getMessage());
		}
		// 发送企微消息
		String disableTypes = crmCpWxConfig.getDisableTypes();
		if (StringUtils.isNotBlank(disableTypes)) {
			List<String> disableTypeList = Arrays.asList(disableTypes.split(","));
			if (disableTypeList.contains(businessType)) {
				log.warn("审批类型【{}】未开启企微推送", businessType);
				return;
			}
		}
		AppContextUtil.getBean(BpmnMsgServiceImpl.class).sendCpWxMessageHandleException(businessType, businessId, bizStatus, groupUsers, procInstId);
	}

	@Async("defaultThreadPoolExecutor")
	public void sendCpWxMessageHandleException(String businessType, String businessId, BizApprovalStatus bizStatus, List<String> groupUsers, String procInstId){
		try {
			this.sendCpWxMessage(businessType, businessId, bizStatus, groupUsers, procInstId);
		} catch (Exception e) {
			log.error("发送企微消息异常", e);
		}
	}

	public void sendCpWxMessage(String businessType, String businessId, BizApprovalStatus bizStatus, List<String> groupUsers, String procInstId) throws UnsupportedEncodingException {
		log.info("接收消息的用户：{}", groupUsers);
		if (CollectionUtils.isEmpty(groupUsers)) {
			log.error("发送企微用户的消息为为空");
			return;
		}
		String msgTitle = this.getMsgTitle(businessType);
		String msgContent = this.getMsgContent(bizStatus.getBusinessTitle(), bizStatus.getCurrStatus(), bizStatus.getApprovedByName(), bizStatus.getApprovedByDept());
		List<AdminDTO> adminDTOS = adminFeignSvc.listByUsername(groupUsers);
		String employeeNums = adminDTOS.stream().map(AdminDTO::getEmployeeNum).collect(Collectors.joining("|"));
		String bpmnUrl = this.buildBpmnUrl(businessType, businessId);
		String crmCallbackUrl = this.buildCrmCallbackUrl(bpmnUrl);
		String crmCpwxCallbackUrl = this.buildCrmCpwxCallbackUrl(crmCallbackUrl);
		log.info("回调地址：{}", crmCpwxCallbackUrl);
		// 建消息体
		WxCpMessage wxCpMessage = WxCpMessage.TEXTCARD()
				.toUser(employeeNums)
				.title(msgTitle)
				.agentId(crmCpWxConfig.getAgentId())
				.description(msgContent)
				.url(crmCpwxCallbackUrl)
				.build();
		cpWxAgentMessageService.sendTextCardMessage(wxCpMessage);
	}

	private String buildCrmCpwxCallbackUrl(String crmCallbackUrl) throws UnsupportedEncodingException {
		String encodeCrmCallbackUrl = URLEncoder.encode(crmCallbackUrl, "UTF-8");
		return String.format(CRM_CPWX_CALLBACK_URL, wxCpProperties.getCorpId(), encodeCrmCallbackUrl, crmCpWxConfig.getAgentId());
	}

	private String buildCrmCallbackUrl(String bpmnUrl) throws UnsupportedEncodingException {
		String encodeBpmnUrl = URLEncoder.encode(bpmnUrl, "UTF-8");
		return String.format(CRM_CALLBACK_URL, crmCpWxConfig.getCallbackHost(), crmCpWxConfig.getCallbackUr(), encodeBpmnUrl, crmCpWxConfig.getAgentId());
	}

	private String buildBpmnUrl(String businessType, String businessId) {
		String bpmnUrl = String.format(BPMB_URL, crmCpWxConfig.getCallbackHost(), businessType);
		return bpmnUrl + businessId;
	}

	private void sendCrmSysMsg(String businessType, String businessId, BizApprovalStatus bizStatus, List<String> groupUsers, String procInstId) {
		if (bizStatus == null) {
			bizStatus = bizStatusSvc.getApprovalStatusByTypeAndId(businessType, businessId, procInstId);
			if (bizStatus == null) {
				return;
			}
		}
		SysMsgCreateDTO sysMsg = new SysMsgCreateDTO();
		sysMsg.setCreateBy("BPMN");
		sysMsg.setMsgType(MsgTypeEnum.AUDIT.getType());
		sysMsg.setMsgBizSource(getMsgBizSource(businessType));
		sysMsg.setMsgRead(0);
		sysMsg.setMsgTitle(getMsgTitle(businessType, businessId, bizStatus.getBusinessTitle(), bizStatus.getCurrStatus(), bizStatus.getProcessType()));
		sysMsg.setMsgContent(getMsgContent(businessType, businessId, bizStatus.getBusinessTitle(), bizStatus.getCurrStatus(),
				bizStatus.getApprovedByName(), bizStatus.getApprovedByDept()));

		sysMsg.setBizType(businessType);
		sysMsg.setBusinessId(businessId);
		sysMsg.setBizStatus(bizStatus.getCurrStatus());
		sysMsg.setOpsCode(bizStatus.getProcessType());

		if (CollUtil.isEmpty(groupUsers)) {
			if (ProcessConstant.Status.REJECTED.equals(bizStatus.getCurrStatus())
					|| ProcessConstant.Status.APPROVED.equals(bizStatus.getCurrStatus())) {
				groupUsers = bizStatus.getRequestBy() != null? Arrays.asList(bizStatus.getRequestBy().split(MktConstants.LIST_STRING_SEPERATOR)):null;
			} else {
				groupUsers = bizStatus.getNextApprover() != null? Arrays.asList(bizStatus.getNextApprover().split(MktConstants.LIST_STRING_SEPERATOR)):null;
			}
		}
		if (groupUsers != null) {
			groupUsers.forEach(e-> {
				Long receiveById = adminFeignSvc.getUserIdByName(e);
				sysMsg.setReceiveById(receiveById);

				sysMsg.setReceiveBy(adminFeignSvc.getUserRealName(e));

				adminFeignSvc.sendMsg(sysMsg);

			});
		}
	}

	private void sendOaMsg(String businessType, String businessId, BizApprovalStatus bizStatus, List<String> groupUsers, String procInstId) {
		if (bizStatus == null) {
			bizStatus = bizStatusSvc.getApprovalStatusByTypeAndId(businessType, businessId, procInstId);
			if (bizStatus == null) {
				return;
			}
		}

		CustomMessageReq customMessageReq = new CustomMessageReq();
		customMessageReq.setTitle(getMsgTitle(businessType, businessId, bizStatus.getBusinessTitle(), bizStatus.getCurrStatus(), bizStatus.getProcessType()));
		customMessageReq.setContext(getMsgContent(businessType, businessId, bizStatus.getBusinessTitle(), bizStatus.getCurrStatus(),
				bizStatus.getApprovedByName(), bizStatus.getApprovedByDept()));
		customMessageReq.setBizState(OA_MSG_STATE_INIT);
		customMessageReq.setTargetId(getOaTargetId(businessType, businessId));

		if (CollUtil.isEmpty(groupUsers)) {
			if (ProcessConstant.Status.REJECTED.equals(bizStatus.getCurrStatus())
					|| ProcessConstant.Status.APPROVED.equals(bizStatus.getCurrStatus())) {
				groupUsers = bizStatus.getRequestBy() != null? Arrays.asList(bizStatus.getRequestBy().split(MktConstants.LIST_STRING_SEPERATOR)):null;
			} else {
				groupUsers = bizStatus.getNextApprover() != null? Arrays.asList(bizStatus.getNextApprover().split(MktConstants.LIST_STRING_SEPERATOR)):null;
			}
		}
		List<String> workCodeList = new ArrayList<>();
		if (groupUsers != null) {
			groupUsers.forEach(e-> {
				AdminDTO user = adminFeignSvc.getByUsername(e);

				if (user != null && com.kingmed.kmss.common.utils.StringUtils.isNotEmpty(user.getEmployeeNum())) {
					workCodeList.add(user.getEmployeeNum());
				}
			});
		}

		if (CollUtil.isEmpty(workCodeList)) {
			log.error("找不到对应用户的员工号，无法发送oa消息: " + groupUsers);
			return;
		}

		customMessageReq.setLinkUrl("/");
		customMessageReq.setWorkCodeList(StringUtils.join(workCodeList, CommonConstants.SEPARATE_COMMA));
		//给oa发消息
		authFeignSvc.sendOaMsg(customMessageReq);
	}

	/**
	 * bizKey转格式:bizType|bizId
	 *
	 * @param businessType
	 * @param businessId
	 * @return
	 */
	private String getOaTargetId(String businessType, String businessId) {
		if (StringUtils.isEmpty(businessType) || StringUtils.isEmpty(businessId)) {
			return null;
		}

		return String.format("%s|%s", businessType, businessId);
	}

	private String getMsgContent(String businessType, String businessId, String bizTitle, Integer status,
			String processedBy, String processedByDept) {
		String msgContent = null;
		if (ProcessConstant.Status.SUBMITTED.equals(status)
				|| ProcessConstant.Status.APPROVING.equals(status)) {
			msgContent = String.format(CONTENT_FORMAT_SUBMITTED, bizTitle);
		} else if (ProcessConstant.Status.REJECTED.equals(status)) {
			msgContent = String.format(CONTENT_FORMAT_REJECTED, bizTitle, processedBy, processedByDept);		
		} else if (ProcessConstant.Status.APPROVED.equals(status)) {
			msgContent = String.format(CONTENT_FORMAT_APPROVED, bizTitle);		
		}
		return msgContent;
	}

	private String getMsgContent(String bizTitle, Integer status, String processedByName, String processedByDept) {
		log.info("标题：{}，审批结果：{}，处理人：{}，部门：{}", bizTitle, status, processedByName, processedByDept);
		String msgContent = null;
		if (ProcessConstant.Status.SUBMITTED.equals(status)
				|| ProcessConstant.Status.APPROVING.equals(status)) {
			msgContent = String.format(CONTENT_FORMAT_SUBMITTED, bizTitle);
		} else if (ProcessConstant.Status.REJECTED.equals(status)) {
			msgContent = String.format(CONTENT_FORMAT_REJECTED, bizTitle, processedByName, processedByDept);
		} else if (ProcessConstant.Status.APPROVED.equals(status)) {
			msgContent = String.format(CONTENT_FORMAT_APPROVED, bizTitle);
		}
		return msgContent;
	}

	private String getMsgTitle(String businessType, String businessId, String bizTitle, Integer status,
			Integer processType) {
		Map<String, String> bizTypeMap = dictFeignSvc.getSysDictMap(ProcessConstant.DictCode.PROCESS_BUSINESS_TYPE);
		return String.format(TITLE_FORMAT, bizTypeMap.get(businessType));
	}

	private String getMsgTitle(String businessType) {
		Map<String, String> bizTypeMap = dictFeignSvc.getSysDictMap(ProcessConstant.DictCode.PROCESS_BUSINESS_TYPE);
		return String.format(TITLE_FORMAT, bizTypeMap.get(businessType));
	}

	private Integer getMsgBizSource(String businessType) {

		List<MsgBizSourceEnum> bizSource = MsgBizSourceEnum.stream()
				.filter(e -> (businessType.toUpperCase().contains(e.name()))).collect(Collectors.toList());
		return bizSource != null ? bizSource.get(0).getType() : null;
	}
	
	public void markMsgAsRead(String businessType, String businessId, Long receivedById) {

		SysMsgDTO sysMsg = new SysMsgDTO();
	
		sysMsg.setMsgType(MsgTypeEnum.AUDIT.getType());
	
		sysMsg.setBizType(businessType);
		sysMsg.setBusinessId(businessId);
		sysMsg.setReceiveById(receivedById);
		
		adminFeignSvc.markMsgAsReadForBiz(sysMsg);
	}
}
